﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Events;
using zFrame.ThreadEx;
using static ServerConfiguration;

namespace zFrame.Networks
{
    public class NetworkClient : MonoSingleton<NetworkClient>
    {

        [SerializeField] ServerConfiguration configuration;
        public UnityEvent m_OnDisconnected = new UnityEvent();
        public static UnityEvent OnDisconnected
        {
            get { return Instance?.m_OnDisconnected; }
        }
        EventSystem eventSystem = new EventSystem();
        ServerAddress serverAddress;
        #region API for handle network event 
        [EditorButton]
        private void TestDispatchEvent(string responseCode )
        {
            Response response = new Response { headCode = HexStr2Hex(responseCode), message = "Event File From NetworkClient.TestDispatchEvent" };
            eventSystem.Invoke(response);
        }

        public static int HexStr2Hex(string src)
        {
            if (!Regex.IsMatch(src, @"^(0[xX])?[0-9a-fA-F]{6}$"))
            {
                throw new InvalidOperationException($"{src} 格式不对，必须使用 16进制的写法 ，形如 0x000001 或者不需要 0x 也行。");
            }
            return Convert.ToInt32(src, 16);
        }

        /// <summary>
        /// 通过此接口向服务器发送消息
        /// </summary>
        /// <param name="requestData"></param>
        public static void SendMessage(Request request)
        {
            Debug.Log($"<color=cyan>[SendMsg]</color> HeadCode :0x{request.headCode.ToString("X6")} ,MessageContent :{request.messageContent}");
            Instance.SendData(request.ToBytes());
        }

        public static void AddListener(ResponseCode code, Action<Response> action)
        {
            Instance.eventSystem.AddListener(code, action);
        }

        public static void RemoveListener(ResponseCode code, Action<Response> action)
        {
            Instance?.eventSystem?.RemoveListener(code, action);
        }

        #endregion

        #region Mono Functions
        public override void OnInit()
        {
            base.OnInit();
            InitHeartbeatData();
            if (!configuration )
            {
                configuration = Resources.Load<ServerConfiguration>("ServerConfiguration");
            }
        }

        public override void OnDestroy()
        {
            eventSystem.Clear();
            DisConnect();
            //SendMessage(new LogoutRequest());
            base.OnDestroy();
        }
        #endregion
        #region Socket 流读取逻辑
        int waitLen = 0;
        bool isWait = false;
        TcpClient tcpclient = new TcpClient();
        //网络流
        NetworkStream stream;
        int port;
        string host;
        byte[] sources;
        byte[] heartbeatBytes;

        private int disConnectCount = 0;

        public static bool hasStartTimer = false;
        public static bool isConnected = false;

        public void InitHeartbeatData()
        {
            HeartbeatRequest heart = new HeartbeatRequest();
            heartbeatBytes = heart.ToBytes();
        }

        public static void Connect()
        {
            Instance.ConnectServer();
        }
        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <param name="ip">服务器IP</param>
        /// <returns></returns>
        public void ConnectServer()
        {
            serverAddress = serverAddress??configuration.GetServerInfo();
            host = serverAddress.host;
            port = serverAddress.ports[0].port;

            IPAddress ipaddress = GetIPAddress(serverAddress.host)[0];
            AddressFamily family = AddressFamily.InterNetwork;
            Debug.LogWarning(host +" : " +ipaddress + " : " + serverAddress.ports[0].port);

            //ipv4转ipv6 - 苹果ios
#if !UNITY_EDITOR && UNITY_IPHONE
		        if (ipaddress.ToString().Contains(":")) {
                    family = AddressFamily.InterNetworkV6;
                }
#endif

            try
            {
                tcpclient = new TcpClient(family);
                // 不使用 Nagle 算法， 每个数据包直接发送不等待&粘连其他数据包
                tcpclient.NoDelay = true;
                tcpclient.BeginConnect(ipaddress, port, new AsyncCallback(ConnectCallback), tcpclient);
            }
            catch (Exception ex)
            {
                //设置标志,连接服务端失败!
                NoticeDisconnect();
                Debug.Log("----------------Connect------------------------Exception----------------");
                Debug.Log(ex.ToString());
                isConnected = false;
            }
        }

        private IPAddress[] GetIPAddress(string url)
        {
            return Dns.GetHostAddresses(url);
        }

        /// <summary>
        /// 关闭网络流
        /// </summary>
        private void DisConnect()
        {
            if (tcpclient != null)
            {
                tcpclient.Close();
                tcpclient = null;
            }
            if (stream != null)
            {
                stream.Close();
                stream = null;
            }
        }


        private void NoticeDisconnect()
        {
            UnitySynchronizationContext.Post(() => OnDisconnected?.Invoke());
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        private void SendData(byte[] data)
        {
            try
            {
                if (stream != null)
                {
                    stream.Write(data, 0, data.Length);
                    Debug.Log("---------------- SendMsg Success ! -------------");
                }
                else
                {
                    Debug.Log("---------------- SendMsg Fail as server disconnect ! -------------");
                    isConnected = false;
                    NoticeDisconnect();
                }
            }
            catch (Exception ex)
            {
                Debug.Log("----------------SendData Exception : " + ex.ToString());
                isConnected = false;
                NoticeDisconnect();
            }

        }

        public static void SendHeartbeatData()
        {
            Instance.SendHeartData();
        }

        private const float heartbeatFrequence = 20f;
        /// <summary>
        /// 发送心跳包
        /// </summary>
        private bool SendHeartData()
        {
            try
            {
                if (stream != null && tcpclient.Connected)
                {
                    stream.Write(heartbeatBytes, 0, heartbeatBytes.Length);
                    string result = String.Join("", heartbeatBytes);
                    Debug.Log($"Send heart data success {result}");
                    xinTiaoTime = DateTime.UtcNow;
                    return true;
                }
                else
                {
                    Debug.Log("Send heart data Failed!");
                    isConnected = false;
                    NoticeDisconnect();
                    return false;
                }
            }
            catch (Exception ex)
            {
                Debug.Log($"Send heart data Failed : {ex.ToString()}");
                isConnected = false;
                NoticeDisconnect();
                return false;
            }
        }

        /// <summary>
        /// 异步连接的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private int reconnectCount = 0;
        private const int RECONNECT_COUNT = 3;
        /// <summary>
        /// 是否已经重连
        /// </summary>
        public static bool hasReconnected = false;
        private void ConnectCallback(IAsyncResult ar)
        {
            //connectDone.Set();
            if ((tcpclient != null) && (tcpclient.Connected))
            {
                stream = tcpclient.GetStream();
                asyncread(tcpclient);
                isConnected = true;
                InvokeRepeating("SendHeartbeat", heartbeatFrequence, heartbeatFrequence); //一旦连接成功，发送心跳包
                Debug.Log("服务器已经连接!");
                Debug.Log("---------------ConnectCallback----------------ConnectCallback--");
            }
            else if (reconnectCount < RECONNECT_COUNT)
            {
                //重新连接
                reconnectCount++;
                tcpclient.BeginConnect(GetIPAddress(host)[0], port, new AsyncCallback(ConnectCallback), tcpclient);
                hasReconnected = false;
            }
            else
            {
                reconnectCount = 0;
                hasReconnected = true;
                NoticeDisconnect();
                //重连后没有连接上
            }

            TcpClient t = (TcpClient)ar.AsyncState;
            try
            {
                t.EndConnect(ar);
            }
            catch (Exception ex)
            {
                if (hasReconnected)//最后一次重连失败就重置重连记录
                {
                    tcpclient.Dispose();
                    tcpclient = null;
                }
                //设置标志,连接服务端失败!
                Debug.Log($"[ConnectCallback Exception] - {ex.ToString()}");
            }
        }
        /// <summary>
        /// 异步读TCP数据
        /// </summary>
        /// <param name="sock"></param>
        private void asyncread(TcpClient sock)
        {
            StateObject state = new StateObject();
            state.client = sock;
            NetworkStream stream;
            try
            {
                stream = sock.GetStream();
                if (stream.CanRead)
                {
                    try
                    {
                        IAsyncResult ar = stream.BeginRead(state.buffer, 0, StateObject.BufferSize,
                                new AsyncCallback(TCPReadCallBack), state);

                    }
                    catch (Exception ex)
                    {
                        //设置标志,连接服务端失败!
                        Debug.Log("---------------连接异常-------------1-----");
                        Debug.Log(ex.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                //设置标志,连接服务端失败!
                // NetManaged.isConnectServer = false;
                // NetManaged.surcessstate = 0;
                Debug.Log(ex.ToString());
                Debug.Log("---------------连接异常-------------2-----");
            }

        }

        /// <summary>
        /// TCP读数据的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void TCPReadCallBack(IAsyncResult ar)
        {
            StateObject state = (StateObject)ar.AsyncState;
            //主动断开时
            if ((state.client == null) || (!state.client.Connected))
            {
                Debug.Log("---------------TCPReadCallBack----------------closeSocket--");
                CloseSocket();
                return;
            }
            int numberOfBytesRead;
            NetworkStream mas = state.client.GetStream();
            numberOfBytesRead = mas.EndRead(ar);
            state.totalBytesRead += numberOfBytesRead;
            if (numberOfBytesRead > 0)
            {
                byte[] dd = new byte[numberOfBytesRead];
                Array.Copy(state.buffer, 0, dd, 0, numberOfBytesRead);
                if (isWait)
                {
                    byte[] temp = new byte[sources.Length + dd.Length];
                    sources.CopyTo(temp, 0);
                    dd.CopyTo(temp, sources.Length);
                    sources = temp;
                    if (sources.Length >= waitLen)
                    {
                        ReceiveCallBack(sources.Clone() as byte[]);
                        isWait = false;
                        waitLen = 0;
                    }
                }
                else
                {
                    sources = null;
                    ReceiveCallBack(dd);
                }
                mas.BeginRead(state.buffer, 0, StateObject.BufferSize,
                        new AsyncCallback(TCPReadCallBack), state);
            }
            else
            {
                //被动断开时 
                mas.Close();
                state.client.Close();
                mas = null;
                state = null;
                //设置标志,连接服务端失败!
                Debug.Log("---------------连接异常-------------3-----");
                Debug.Log("客户端被动断开");
            }
        }

        /// <summary>
        /// 读取大端序的int
        /// </summary>
        /// <param name="value"></param>
        public int ReadInt(byte[] intbytes)
        {
            Array.Reverse(intbytes);
            return BitConverter.ToInt32(intbytes, 0);
        }

        public ushort ReadShort(byte[] intbytes)
        {
            Array.Reverse(intbytes);
            return BitConverter.ToUInt16(intbytes, 0);
        }

        private void ReceiveCallBack(byte[] m_receiveBuffer)
        {
            //通知调用端接收完毕
            try
            {
                //	Debug.Log("m_receiveBuffer======"+m_receiveBuffer.Length);
                MemoryStream ms = new MemoryStream(m_receiveBuffer);
                BinaryReader buffers = new BinaryReader(ms, UTF8Encoding.Default);
                ReadBuffer(buffers);
            }
            catch (Exception ex)
            {
                Debug.Log("---------------连接异常-------------4-----");
                Debug.Log("socket exception:" + ex.Message);
                throw new Exception(ex.Message);
            }
        }

        private void ReadBuffer(BinaryReader buffers)
        {
            byte flag = buffers.ReadByte();
            int lens = ReadInt(buffers.ReadBytes(4));
            disConnectCount = 0;
            if (!hasStartTimer && lens == 16)
            {
                startTimer();
                hasStartTimer = true;
            }

            long sLens = buffers.BaseStream.Length - buffers.BaseStream.Position + 5;
            if (lens > sLens)
            {
                //Debug.Log("lens:" + lens + "剩余:" + sLens);
                waitLen = lens;
                isWait = true;
                buffers.BaseStream.Position -= 5;
                byte[] dd = new byte[sLens];
                byte[] temp = buffers.ReadBytes((int)sLens);
                Array.Copy(temp, 0, dd, 0, (int)sLens);
                if (sources == null)
                {
                    sources = dd;
                }
                return;
            }

            int headcode = ReadInt(buffers.ReadBytes(4));       //协议： 1位 flag ，4位 消息长度，4位消息头，4位状态码，2位消息长度，
            int status = ReadInt(buffers.ReadBytes(4));
            int messageLen = ReadInt(buffers.ReadBytes(4));
            if (flag == 1)
            {
                byte[] messByte = buffers.ReadBytes(messageLen);
                string message = Encoding.UTF8.GetString(messByte);
                Response response = new Response
                {
                    status = status,
                    message = message,
                    headCode = headcode
                };
                string newline = Environment.NewLine;
                Debug.Log($"<color=green>[ReceiveMsg]</color>→ HeadCode = 0x{response.headCode.ToString("X6") }  ↓选中可见更多内容↓{newline}"
                                 + $"Time= {DateTime.Today.ToString("HH:mm:ss:ms")}{newline} "
                                 + $"lengs = { lens }{newline} "
                                 + $"messageLengs = { messageLen }{newline}"
                                 + $"字节数:{ messByte.Length }{newline}"
                                 + $"response.message = {message}");
                eventSystem.Invoke(response);
            }
            if (buffers.BaseStream.Position < buffers.BaseStream.Length)
            {
                ReadBuffer(buffers);
            }

        }

        public void CloseSocket()
        {
            DisConnect();
        }

        System.Timers.Timer t;
        private DateTime xinTiaoTime;

        private void startTimer()
        {
            if (t == null)
            {
                t = new System.Timers.Timer(20000);   //实例化Timer类，设置间隔时间为10000毫秒；   
                t.Elapsed += new System.Timers.ElapsedEventHandler(timeout); //到达时间的时候执行事件；   
                t.AutoReset = true;   //设置是执行一次（false）还是一直执行(true)；   
                t.Enabled = true;     //是否执行System.Timers.Timer.Elapsed事件；   
            }
            else
            {

                t.Start();
            }

        }


        public void timeout(object source, System.Timers.ElapsedEventArgs e)
        {
            //Debug.Log ("disConnectCount+" + disConnectCount);
            disConnectCount += 1;
            if (disConnectCount >= 3)
            {
                t.Stop();

                disConnectCount = 0;
                //	closeSocket ();
                isConnected = false;
                NoticeDisconnect();
                return;

            }
        }
        internal class StateObject
        {
            public TcpClient client = null;
            public int totalBytesRead = 0;
            public const int BufferSize = 1024 * 1024 * 2;
            public string readType = null;
            public byte[] buffer = new byte[BufferSize];
        }
        #endregion


        #region 内建事件分发系统
        internal class EventSystem
        {
            /// <summary>
            /// 事件链
            /// </summary>
            private Dictionary<int, List<Action<Response>>> callbacks = new Dictionary<int, List<Action<Response>>>();

            public void AddListener(ResponseCode code, Action<Response> action)
            {
                int key = (int)code;
                if (callbacks.ContainsKey(key))
                {
                    List<Action<Response>> actions = callbacks[key];
                    if (!actions.Contains(action))
                    {
                        actions.Add(action);
                    }
                }
                else
                {
                    Debug.Log($"Enum.IsDefined : {Enum.IsDefined(typeof(ResponseCode), key) } ,key : {key.ToString("X")}");
                    callbacks.Add(key, new List<Action<Response>>() { action });
                }
            }

            /// <summary>
            /// 移除监听
            /// </summary>
            public void RemoveListener(ResponseCode code, Action<Response> action)
            {
                int key = (int)code;
                if (callbacks.ContainsKey(key))
                {
                    List<Action<Response>> act = callbacks[key];
                    act.Remove(action);
                    if (act.Count == 0) callbacks.Remove(key);
                }
            }

            /// <summary>
            /// 执行事件
            /// </summary>
            public void Invoke(Response response)
            {
                int key = response.headCode;
                if (callbacks.ContainsKey(key))
                {
                    List<Action<Response>> act = callbacks[key];
                    foreach (var item in act)
                    {
                        UnitySynchronizationContext.Post(() => item?.Invoke(response));
                    }
                }
            }


            public void Clear()
            {
                foreach (var item in callbacks)
                {
                    item.Value.Clear();
                }
                callbacks.Clear();
            }
        }
        #endregion
    }
}
